/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：登录通-数据源-外部接口或第三方平台-接口声明
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taiping.dlt.client.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.taiping.dlt.application.rmi.WebClientKit;
import com.taiping.dlt.pojo.bo.AuthLoginBO;
import com.taiping.dlt.pojo.dto.AuthLoginDTO;
import com.taiping.dlt.pojo.properties.DltProperty;
import com.taiping.dlt.pojo.query.AuthLoginQO;
import com.taipingframework.boot.web.response.result.ApiResult;
import com.taipingframework.utility.constant.ApplicationConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.List;

//import feign.Headers;
//import feign.Param;
//import feign.RequestLine;
//import reactor.core.publisher.Mono;
//
//@Headers({"Accept: application/json"})
//public interface AuthorizeServiceClient {
//
//    /**
//     * 腾讯定位服务，获取位置信息
//     */
//    @Headers("Content-Type: application/json")
//    @RequestLine("GET /ws/location/v1/ip?key={key}")
//    Mono<String> getIp(@Param("key") String key);
//
//}
@Slf4j
@Service
public class AuthorizeServiceClient {

    /**
     * 重试次数
     */
    private static final int RETRY = 3;

    private static ObjectMapper objectMapper;
    // private static HttpSessionProperty httpSessionProperty;
    private static DltProperty dltProperty;

    @Autowired
    public AuthorizeServiceClient(@Qualifier("objectMapper") ObjectMapper objectMapper,
                                  // HttpSessionProperty httpSessionProperty,
                                  DltProperty dltProperty) {
        AuthorizeServiceClient.objectMapper = objectMapper;
        // AuthorizeServiceClient.httpSessionProperty = httpSessionProperty;
        AuthorizeServiceClient.dltProperty = dltProperty;
    }

    /**
     * 远程调用微服务接口 - 登录认证
     */
    public static Mono<AuthLoginBO> loginClientHandler(AuthLoginQO authLoginQO) {
        return Mono.deferWithContext(Mono::just)
                .map(context -> {
                    ServerWebExchange serverWebExchange = context.get(ServerWebExchange.class);
                    String requestId = context.get(ApplicationConstant.RESPONSE_IDENTITY);

                    return WebClientKit.createWebClient(serverWebExchange, dltProperty.getLocalBaseUrl(), objectMapper)
                            .post()
                            .uri(dltProperty.getLocalValidPath())
                            .body(BodyInserters.fromValue(authLoginQO))
                            // 登录接口 不会携带token
                            //.header(httpSessionProperty.getHeaderName(), StringUtils.EMPTY)
                            .retrieve()
                            /*.onStatus(HttpStatus::is4xxClientError, resp ->
                                    Mono.error(new RuntimeException("client error - " + resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase())))
                            .onStatus(HttpStatus::is5xxServerError, resp ->
                                    Mono.error(new RuntimeException("server error - " + resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase())))*/
                            .bodyToMono(new ParameterizedTypeReference<ApiResult<AuthLoginBO>>() {
                            })
                            // 请求失败时(例如在请求执行过程中发生了异常)，重试策略
                            .retryWhen(Retry.backoff(RETRY, Duration.ofSeconds(1)))
                            .subscribeOn(Schedulers.boundedElastic())
                            .log()
                            .map(apiResult -> WebClientKit.apiResultHandle(apiResult, requestId, dltProperty.getLocalBaseUrl(), dltProperty.getLocalValidPath()))
                            //.doOnError(error -> log.error("[" + requestId + "]error {}", error.getMessage(), error))
                            // 接口调用失败后的默认返回值，等同于OpenFeign服务降级处理
                            //.onErrorReturn(false)
                            // 遇到异常之后，对异常进行捕获、包装、并重新抛出错误
                            .onErrorResume(e -> Mono.error(WebClientKit.newErrorResume(e)))
                            .doFinally(signalType -> log.info("[" + requestId + "]后端登录接口调用结束！"));
                })
                .flatMap(authLoginBOMono -> authLoginBOMono);
    }

    /**
     * 远程调用微服务接口 - 推送数据
     */
    public static Mono<Boolean> pushClientHandler(List<AuthLoginBO> authLoginBOList) {
        AuthLoginDTO authLoginDTO = new AuthLoginDTO();
        authLoginDTO.setAuthLoginBOList(authLoginBOList);

        return Mono.deferWithContext(Mono::just)
                .map(context -> {
                    ServerWebExchange serverWebExchange = context.get(ServerWebExchange.class);
                    String requestId = context.get(ApplicationConstant.RESPONSE_IDENTITY);

                    return WebClientKit.createWebClient(serverWebExchange, dltProperty.getLocalBaseUrl(), objectMapper)
                            .post()
                            .uri(dltProperty.getLocalPushPath())
                            .body(BodyInserters.fromValue(authLoginDTO))
                            // 推送数据接口 不会携带token
                            //.header(httpSessionProperty.getHeaderName(), StringUtils.EMPTY)
                            .retrieve()
                            /*.onStatus(HttpStatus::is4xxClientError, resp ->
                                    Mono.error(new RuntimeException("client error - " + resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase())))
                            .onStatus(HttpStatus::is5xxServerError, resp ->
                                    Mono.error(new RuntimeException("server error - " + resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase())))*/
                            .bodyToMono(new ParameterizedTypeReference<ApiResult<Boolean>>() {
                            })
                            .subscribeOn(Schedulers.boundedElastic())
                            .log()
                            .map(apiResult -> WebClientKit.apiResultHandle(apiResult, requestId, dltProperty.getLocalBaseUrl(), dltProperty.getLocalPushPath()))
                            //.doOnError(error -> log.error("[" + requestId + "]error {}", error.getMessage(), error))
                            // 接口调用失败后的默认返回值，等同于OpenFeign服务降级处理
                            //.onErrorReturn(false)
                            // 遇到异常之后，对异常进行捕获、包装、并重新抛出错误
                            .onErrorResume(e -> Mono.error(WebClientKit.newErrorResume(e)))
                            .doFinally(signalType -> log.info("[" + requestId + "]推送登录认证数据接口调用结束！"));
                })
                .flatMap(resultMono -> resultMono);
    }

    /**
     * 远程调用微服务接口 - 禁用账户/(逻辑)删除账户
     */
    public static Mono<Boolean> disableClientHandler(AuthLoginQO loginQO) {
        return Mono.deferWithContext(Mono::just)
                .map(context -> {
                    ServerWebExchange serverWebExchange = context.get(ServerWebExchange.class);
                    String requestId = context.get(ApplicationConstant.RESPONSE_IDENTITY);
                    return WebClientKit.createWebClient(serverWebExchange, dltProperty.getLocalBaseUrl(), objectMapper)
                            .post()
                            .uri(dltProperty.getLocalDisablePath())
                            .body(BodyInserters.fromValue(loginQO))
                            .retrieve()
                            .bodyToMono(new ParameterizedTypeReference<ApiResult<Boolean>>() {
                            })
                            .subscribeOn(Schedulers.boundedElastic())
                            .log()
                            .map(apiResult -> WebClientKit.apiResultHandle(apiResult, requestId, dltProperty.getLocalBaseUrl(), dltProperty.getLocalDisablePath()))
                            .onErrorResume(e -> Mono.error(WebClientKit.newErrorResume(e)))
                            .doFinally(signalType -> log.info("[" + requestId + "]禁用账户接口调用结束！"));
                })
                .flatMap(resultMono -> resultMono);
    }

}
